package com.platform.metamodel;

import cn.hutool.core.convert.Convert;
import com.google.common.base.Converter;
import lombok.Getter;
import org.springframework.beans.BeanUtils;
import org.springframework.data.util.ReflectionUtils;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;

/**
 * @author liujizheng
 * @version 1.0
 * @name ContextMapper
 * @Desc 通用映射器
 * @date 2022/3/8 16:40
 */
@Component
public class ContextMapper {

    public <T> T map(Map<String, Object> map, Class<T> clazz) {
        return map(map, clazz, Converter.identity());
    }

    public <T> T map(Map<String, Object> map, Class<T> clazz, Converter<String, String> converter) {
        return convert(map, clazz, converter);
    }

    public <T> T map(Map<String, Object> map, Class<T> clazz, Mapper mapper) {
        CustomMapper customMapper = new CustomMapper(mapper);
        ContextMappings mappings = customMapper.getMappings();
        Field[] fields = clazz.getDeclaredFields();
        T o = BeanUtils.instantiateClass(clazz);
        Arrays.stream(fields).forEach(field -> {
            String name = field.getName();
            String key = Optional.ofNullable(mappings.keyFunction(name)).orElse(Function.identity()).apply(name);
            Object value = Optional.ofNullable(mappings.valueFunction(name)).orElse(Function.identity()).apply(map.get(key));
            ReflectionUtils.setField(field, o, value);
        });
        return o;
    }

    public static <T> T convert(Map<String, Object> map, Class<T> clazz, Converter<String, String> converter) {
        Field[] fields = clazz.getDeclaredFields();
        T o = BeanUtils.instantiateClass(clazz);
        Arrays.stream(fields).forEach(field -> {
            Class<?> type = field.getType();
            String key = converter.convert(field.getName());
            ReflectionUtils.setField(field, o, Convert.convertWithCheck(type, map.get(key), null, false));
        });
        return o;
    }

    private static class CustomMapper {
        @Getter
        final ContextMappings mappings;
        final Mapper mapper;
        CustomMapper(Mapper mapper) {
            this.mappings = new ContextMappings();
            this.mapper = mapper;
            this.mapper.mapping(mappings);
        }
    }
}
